<?php

declare(strict_types=1);

namespace App\Controller\Admin\V1\Admin;

use App\Controller\Admin\V1\Controllers;
use App\Exception\InvalidRequestException;
use App\Model\Admin\Role as RoleModel;
use App\Request\Admin\Role as RoleRequest;
use App\Services\Admin\Permission as PermissionServices;
use App\Services\Admin\Role as RoleServices;
use Donjan\Casbin\Enforcer;
use Hyperf\Database\Model\Builder;
use Hyperf\Database\Model\Relations\HasMany;
use Psr\Http\Message\ResponseInterface;
use App\Services\Admin\Menu as MenuServices;
use function Hyperf\Translation\__;

class Role extends Controllers
{
    /**
     * @var PermissionServices
     */
    protected $permissionServices;

    /**
     * @var string
     */
    protected string $requestClass = RoleRequest::class;

    public function __construct(
        RoleServices $roleServices,
        PermissionServices $permissionServices,
        protected MenuServices $menuServices
    )
    {
        parent::__construct();

        $this->services = $roleServices;
        $this->permissionServices = $permissionServices;
    }

    /**
     * 分页
     * @return \Psr\Http\Message\ResponseInterface
     */
    public function page()
    {
        $get = $this->request->query();

        [$field, $order] = $this->orderMap();
        $page = $this->services
            ->search($get)
            ->orderBy($field, $order)
            ->paginate($this->getLimit());

        return success([
            'page' => $page
        ]);
    }

    /**
     * 新增页面
     * @return ResponseInterface
     */
    public function create():ResponseInterface
    {
        $permissionMap = [];
        $adminInfo = getInfo();
        if (!getInfo('admin', 'is_admin')) {
            $roles = Enforcer::getRolesForUserInDomain((string)$adminInfo['id'], 'admin');
            if (!$roles) {
                throw new InvalidRequestException(__('message.binding', ['name' => __('model.table.role')]));
            }
            $permissions = [];
            foreach ($roles as $role) {
                $permissionEnforce = Enforcer::getPermissionsForUserInDomain((string)$role, 'admin');
                foreach ($permissionEnforce as $item) {
                    array_push($permissions, "{$item[2]}|{$item[3]}");
                }
            }
            if (!$permissions) {
                return power();
            }
            $permissionMap[] = [
                'id', 'in', $this->permissionServices
                    ->whereRaw('concat_ws("|", path, method) in (?)', [implode(',', $permissions)])
                    ->pluck('id')->toArray()
            ];
        }

        $menu = $this->menuServices
            ->with(['children' => function (HasMany $hasMany) use ($permissionMap) {
                $hasMany->with(['children' => function (HasMany $hasMany) use ($permissionMap) {
                    $hasMany->with(['permission' => function (HasMany $hasMany) use ($permissionMap) {
                        $hasMany->where($permissionMap);
                    }])
                        ->whereHas('permission', function (Builder $builder) use ($permissionMap) {
                            $builder->where($permissionMap);
                        })
                        ->orderBy('sort');
                }])
                    ->whereHas('children', function (Builder $builder) use ($permissionMap) {
                        $builder->whereHas('permission', function (Builder $builder) use ($permissionMap) {
                            $builder->where($permissionMap);
                        });
                    })
                    ->orderBy('sort');
            }])
            ->whereHas('children', function (Builder $builder) use ($permissionMap) {
                $builder->whereHas('children', function (Builder $builder) use ($permissionMap) {
                    $builder->whereHas('permission', function (Builder $builder) use ($permissionMap) {
                        $builder->where($permissionMap);
                    });
                });
            })
            ->where('level', 1)
            ->orderBy('sort')
            ->get();

        return success(compact('menu'));
    }

    /**
     * 新增
     * @return \Psr\Http\Message\ResponseInterface
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    public function save()
    {
        $input = $this->input(['name','desc','status','permission']);

        $permissions = $this->permissionServices
            ->leftJoin('menu', 'permission.menu_id', '=', 'menu.id')
            ->where('menu.app', 'admin')
            ->whereIn('permission.id', $input['permission'])
            ->get(['permission.path', 'method']);
        if ($permissions->isEmpty()) {
            return failed(__('message.permission.not_found'));
        }

        /** @var RoleModel $model */
        $model = $this->services->create($input);
        if (!$model->exists) {
            return internalError(__('message.stored.failed'));
        }

        $model->addPermission($permissions);

        return stored();
    }

    /**
     * 编辑页
     * @param int $id
     * @return ResponseInterface
     */
    public function edit(int $id):ResponseInterface
    {
        $model = $this->services->findOrFail($id);

        $permissionMap = [];
        $permission_list = [];
        $adminInfo = getInfo();
        if (!getInfo('admin', 'is_admin')) {
            $roles = Enforcer::getRolesForUserInDomain((string)$adminInfo['id'], 'admin');
            if (!$roles) {
                return failed(__('message.binding', ['name' => __('model.table.role')]));
            }
            $permissions = [];
            foreach ($roles as $role) {
                $permissionEnforce = Enforcer::getPermissionsForUserInDomain((string)$role, 'admin');
                foreach ($permissionEnforce as $item) {
                    array_push($permissions, "{$item[2]}|{$item[3]}");
                }
            }
            if (!$permissions) {
                return power();
            }

            $permission_list = $this->permissionServices
                ->whereRaw('concat_ws("|", path, method) in (?)', [implode(',', $permissions)])
                ->pluck('id')->toArray();
            $permissionMap[] = [
                'id', 'in', $permission_list
            ];
        }

        $menu = $this->menuServices
            ->with(['children' => function (HasMany $hasMany) use ($permissionMap) {
                $hasMany->with(['children' => function (HasMany $hasMany) use ($permissionMap) {
                    $hasMany->with(['permission' => function (HasMany $hasMany) use ($permissionMap) {
                        $hasMany->where($permissionMap);
                    }])
                        ->whereHas('permission', function (Builder $builder) use ($permissionMap) {
                            $builder->where($permissionMap);
                        })
                        ->orderBy('sort');
                }])
                    ->whereHas('children', function (Builder $builder) use ($permissionMap) {
                        $builder->whereHas('permission', function (Builder $builder) use ($permissionMap) {
                            $builder->where($permissionMap);
                        });
                    })
                    ->orderBy('sort');
            }])
            ->whereHas('children', function (Builder $builder) use ($permissionMap) {
                $builder->whereHas('children', function (Builder $builder) use ($permissionMap) {
                    $builder->whereHas('permission', function (Builder $builder) use ($permissionMap) {
                        $builder->where($permissionMap);
                    });
                });
            })
            ->where('level', 1)
            ->orderBy('sort')
            ->get()
            ->map(function ($item) use ($permission_list) {
                return $item->children->map(function ($children) use ($permission_list) {
                    return $children->children->map(function ($child) use ($permission_list) {
                        $permissionIds = $child->permission->column('id');
                        $child->checkedMenu = [];
                        foreach ($permission_list as $value) {
                            if (in_array($value, $permissionIds)) {
                                $child->checkedMenu[] = $value;
                            }
                        }
                        return $child;
                    });
                });
            });

        return success(compact('menu', 'model', 'permission_list'));
    }

    /**
     * 编辑
     * @param int $id
     * @return ResponseInterface
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    public function update(int $id):ResponseInterface
    {
        $input = $this->input(['name','desc','status','permission', 'test']);

        /** @var RoleModel $model */
        $model = $this->services->findOrFail($id);

        $permissions = $this->permissionServices
            ->leftJoin('menu', 'permission.menu_id', '=', 'menu.id')
            ->where('menu.app', 'admin')
            ->whereIn('permission.id', $input['permission'])
            ->get(['permission.path', 'method']);
        if ($permissions->isEmpty()) {
            return failed(__('message.permission.not_found'));
        }

        if (!$model->fill($input)->save()) {
            return internalError(__('message.updated.failed'));
        }

        $model->updatePermission($permissions);

        return updated();
    }

    /**
     * 删除角色
     * @return \Psr\Http\Message\ResponseInterface
     * @throws \Exception
     */
    public function destroy()
    {
        $input = $this->input(['ids']);
        if (!$this->services->delete($input['ids'])) {
            return internalError(__('message.destroy.failed'));
        }

        return deleted();
    }
}
